﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace chess
{
    public class csChess
    {
        enumFigType[] initFigType = new enumFigType[32] {
            enumFigType.T, enumFigType.S, enumFigType.L, enumFigType.D, enumFigType.K, enumFigType.L, enumFigType.S, enumFigType.T, 
            enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B,
            enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B, enumFigType.B,
            enumFigType.T, enumFigType.S, enumFigType.L, enumFigType.D, enumFigType.K, enumFigType.L, enumFigType.S, enumFigType.T 
        };

        enumFigParty[] initFigParty = new enumFigParty[32] {
            enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black,
            enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black, enumFigParty.Black,
            enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White,
            enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White, enumFigParty.White 
        };

        private csFig[] _figs = new csFig[32];
        private csBoard _board = new csBoard();

        // Wird nach jedem Zug aufgerufen und sollte in der GUI zur Aktualisierung des Spielfeldes
        // verwendet werden
        public delegate void DelegateOnMoveMade();
        public event DelegateOnMoveMade OnMoveMade;

        // Wird unmittelbar vor der Durchführung eines Zuges aufgerufen
        public delegate void DelegateOnGoingToMakeMove(object sender, string move);
        public event DelegateOnGoingToMakeMove OnGoingToMakeMove;

        // Wir aufgerufen, wenn einer der beiden Könige gescfhlagen wurde - welcher Seite dieser
        // angehörte, das steht im Parameter party
        public delegate void DelegateOnKingDead(enumFigParty party);
        public event DelegateOnKingDead OnKingDead;

        // Wird im Falle eines Unentschieden aufgerufen.
        // Beispielsweise wenn beide Seiten nur noch Bauern und Könige im Spiel haben
        public delegate void DelegateOnStandOff();
        public event DelegateOnStandOff OnStandOff;

        public bool _databaseExists = false;
        public string _databaseFile = string.Empty;


        public csChess()
        {
            this.Initialize();
        }

        public void Initialize()
        {
            this._board.Initialize();

            int zeile = 0;
            for (int i = 0; i < 8; i++)
            {
                _figs[i] = new csFig(_board, initFigType[i], initFigParty[i], i, zeile);
            }

            zeile = 1;
            for (int i = 8; i < 16; i++)
            {
                _figs[i] = new csFig(_board, initFigType[i], initFigParty[i], i - 8, zeile);
            }

            zeile = 6;
            for (int i = 16; i < 24; i++)
            {
                _figs[i] = new csFig(_board, initFigType[i], initFigParty[i], i - 16, zeile);
            }

            zeile = 7;
            for (int i = 24; i < 32; i++)
            {
                _figs[i] = new csFig(_board, initFigType[i], initFigParty[i], i - 24, zeile);
            }

            for (int i = 0; i < 32; i++)
            {
                placeFig(_board, _figs[i]);
            }
        }

        public csFig MarkedFigure
        {
            get
            {
                for (int i = 0; i < 32; i++)
                {
                    if (this._figs[i].Marked == true)
                    {
                        return this._figs[i];
                    }
                }
                return null;
            }
        }

        // Markiert Felder im Einzugsbereich einer Figur mit dem übergebenen Tag
        public csPossibleMoveList MarkFields(csFig figure, enumFieldMarkerType marker)
        {
            if (figure == null) return null;

            csPossibleMoveList moveList = this.Evaluate(figure, marker);
            foreach (csPossibleMove move in moveList.GetList)
            {
                csField destField = move.Field;
                switch (marker)
                {
                    case enumFieldMarkerType.destination:
                        this.GetField(destField.Spalte, destField.Zeile).Marked = true;
                        break;
                    case enumFieldMarkerType.offended:
                        this.GetField(destField.Spalte, destField.Zeile).Offended = true;
                        break;
                    default:
                        this.GetField(destField.Spalte, destField.Zeile).Offended = false;
                        this.GetField(destField.Spalte, destField.Zeile).Marked = false;
                        break;
                }
            }
            return moveList;
        }

        // Markiert Felder im Einzugsbereich der Figur auf diesem Feld mit dem übergebenen Tag. 
        public csPossibleMoveList MarkFields(csField field, enumFieldMarkerType markerType)
        {
            if (field != null)
            {
                csFig fig = field.GetFig;
                return this.MarkFields(fig, markerType);
            }
            return null;
        }

        public csPossibleMoveList MarkFields(enumFigParty party, enumFieldMarkerType marker)
        {
            List<csFig> figs = this._board.GetAllActive(party);
            csPossibleMoveList resultPossibleMoveList = new csPossibleMoveList();

            foreach (csFig fig in figs)
            {
                csPossibleMoveList possibleMoveList = this.Evaluate(fig, marker);
                foreach (csPossibleMove move in possibleMoveList.GetList)
                {
                    csField destField = move.Field;
                    switch (marker)
                    {
                        case enumFieldMarkerType.destination:
                            this.GetField(destField.Spalte, destField.Zeile).Marked = true;
                            break;
                        case enumFieldMarkerType.offended:
                            this.GetField(destField.Spalte, destField.Zeile).Offended = true;
                            break;
                        default:
                            this.GetField(destField.Spalte, destField.Zeile).Offended = false;
                            this.GetField(destField.Spalte, destField.Zeile).Marked = false;
                            break;
                    }
                    resultPossibleMoveList.Add(move);
                }
            }

            return resultPossibleMoveList;
        }

        public void UnmarkAllFields()
        {
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    this.GetField(i, j).Marked = false;
                    this.GetField(i, j).Offended = false;
                }
            }
        }

        public bool ManualMove(enumFigParty party, csField field, enumFieldMarkerType marker)
        {
            // Wurde noch keine Figur ausgewählt, dann selektiere die, die sich auf dem
            // übergebenen Feld befindet als aktuelle Figur
            if (this.MarkedFigure == null)
            {
                if (field != null)
                {
                    csFig fig = field.GetFig;
                    if (fig != null)
                    {
                        // Befindet sich auf dem Feld eine Figur, die nicht der übergebenen
                        // Partei entspricht, breche die Routine hier ab
                        if (fig.Party != party) return false;
                        fig.Marked = true;
                    }
                }
            } else
            {
                string strFieldStatus = Convert.ToString(field.Status);
                string strMarkedFigure = Convert.ToString(this.MarkedFigure.Party);

                // ist das selektierte Feld leer oder befindet sich eine gegenersiceh Figure darauf
                // dann führe Zug aus
                if ((field.Status == enumFieldStatus.Empty)
                    || (strFieldStatus != strMarkedFigure))
                {
                    bool moveExecuted = false;
                    csFig markedFigure = this.MarkedFigure;
                    csField startField = this._board.GetField(markedFigure.Spalte, markedFigure.Zeile);

                    csPossibleMoveList pmList = this.Evaluate(startField, marker);
                    foreach(csPossibleMove pMove in pmList.GetList)
                    {
                        if ((pMove.Field.Spalte == field.Spalte)&&(pMove.Field.Zeile == field.Zeile))
                        {
                            if (this.OnGoingToMakeMove != null) this.OnGoingToMakeMove(this, pMove.ToString());
                            this.executeMove(pMove);
                            this.UnmarkAllFigures();
                            moveExecuted = true;
                            if (this.OnMoveMade != null) this.OnMoveMade();
                        }
                    }
                    return moveExecuted;

                } else
                {
                    // Befindet sich eine eigene Figur auf dem selektierten Zielfeld, dann
                    // deselektiere die vorher gewählte Figur und lasse neue Zugplanung zu
                    //if (this._markedFigure != null)
                    if (this.MarkedFigure != null)
                    {
                        csFig currentlyMarkedFig = this.MarkedFigure;
                        currentlyMarkedFig.Marked = false;
                    }
                    // ...und selektiere die neue Figur
                    csFig fig = field.GetFig;
                    if (fig != null)
                    {
                        if (fig.Party != party) return false;
                        fig.Marked = true;
                    }
                }

            }
            return false;
        }

        // Setzt das Markierungflag für alle Figuren zurück, so dass diese im Status "unmarked" sind
        public void UnmarkAllFigures()
        {
            List<csFig> figW = this._board.GetAllActive(enumFigParty.White);
            foreach (csFig fig in figW)
            {
                fig.Marked = false;
            }

            List<csFig> figB = this._board.GetAllActive(enumFigParty.Black);
            foreach (csFig fig in figB)
            {
                fig.Marked = false;
            }
        }

        // Evaluiert die möglichen Züge einer Figur unabhängig von der Farbe dieser Figur.
        public csPossibleMoveList Evaluate(csFig fig, enumFieldMarkerType marker)
        {
            csPossibleMoveList possibleMoveList = new csPossibleMoveList();

            List<csField> fields = fig.evaluateMoves(marker);
            foreach (csField listField in fields)
            {
                csPossibleMove possibleMove = new csPossibleMove(fig, listField);
                possibleMoveList.Add(possibleMove);
            }

            return possibleMoveList;
        }

        // Evaluiert die möglichen Züge der auf dem Feld "field" befindlichen Figur 
        // unabhängig von der Farbe dieser Figur.
        public csPossibleMoveList Evaluate(csField field, enumFieldMarkerType marker)
        {
            csFig fig = field.GetFig;
            return this.Evaluate(fig, marker);
        }

        // Evaluiert alle möglichen Züge der Steine einer Farbe übergreifend über alle Figurtypen
        public csPossibleMoveList Evaluate(enumFigParty party, enumFieldMarkerType marker)
        {
            List<csFig> figs = this._board.GetAllActive(party);
            csPossibleMoveList resultPossibleMoveList = new csPossibleMoveList();

            foreach (csFig fig in figs)
            {
                csPossibleMoveList possibleMoveList = this.Evaluate(fig, marker);
                foreach (csPossibleMove move in possibleMoveList.GetList)
                {
                    resultPossibleMoveList.Add(move);
                }
            }

            return resultPossibleMoveList;
        }

        public string getSituationAsString
        {
            get
            {
                string strPos = "";
                for (int j = 0; j < 8; j++)
                {
                    for (int i = 0; i < 8; i++)
                    {
                        csField field = this._board.GetField(i, j);
                        csFig fig = field.GetFig;
                        if (fig != null)
                        {
                            strPos += fig.getDescription + "|";
                        }
                    }
                }
                return strPos;
            }
        }

        public void setDescription(string strDescr)
        {
            for (int j = 0; j < 8; j++)
            {
                for (int i = 0; i < 8; i++)
                {
                    this._board.GetField(i, j).ClearField();
                }
            }

            string[] arrDescr = strDescr.Split('|');
            foreach(string strFigDescr in arrDescr)
            {
                string strFigParty = Convert.ToString(strFigDescr[0]);
                // Jetzt liest die Rountine in die Boardbewertung rein...
                if (strFigParty == "[") return;

                string strFigType = Convert.ToString(strFigDescr[1]);
                int spalte = Convert.ToInt32(strFigDescr[2]) - 65;
                int zeile = 7 - (Convert.ToInt32(strFigDescr[3]) - 49);

                csFig newFig = new csFig(this._board, strFigType, strFigParty, spalte, zeile);
                this._board.placeFig(newFig);
            }
        }

        public int EvaluateSituationFor(enumFigParty party)
        {
            return this._board.EvaluateSituationFor(party);
        }

        public void ExecuteMove(enumFigParty party, enumFieldMarkerType marker)
        {
            bool raiseKingDeadEvent = false;

            // Markiere angreifbare Felder
            switch (party)
            {
                case enumFigParty.White:
                    MarkFields(enumFigParty.Black, enumFieldMarkerType.offended);
                    MarkFields(enumFigParty.White, enumFieldMarkerType.destination);
                    break;
                case enumFigParty.Black:
                    MarkFields(enumFigParty.White, enumFieldMarkerType.offended);
                    MarkFields(enumFigParty.Black, enumFieldMarkerType.destination);
                    break;
                default:
                    MarkFields(enumFigParty.White, enumFieldMarkerType.none);
                    break;
            }

            csPossibleMoveList possibleMoves = this.Evaluate(party, marker);
            csPossibleMove move = possibleMoves.GetMoveWithHighestValue;

            // Entferne Feldmarkierungen
            this.UnmarkAllFields();

            // Auswertung, ob mit diesem Zug der König geschlagen wird
            raiseKingDeadEvent = false;
            csFig fig = null;
            fig = move.Field.GetFig;
            if (fig != null)
            {
                if (fig.FigType == enumFigType.K)
                {
                    raiseKingDeadEvent = true;
                }
            }

            if (this.OnGoingToMakeMove != null) this.OnGoingToMakeMove(this, move.ToString());
            this.executeMove(move);

            if ((this.OnKingDead != null) && (raiseKingDeadEvent == true))
            {
                this.executeMove(move);
                switch (party)
                {
                    case enumFigParty.White:
                        OnKingDead(enumFigParty.Black);
                        break;
                    case enumFigParty.Black:
                        OnKingDead(enumFigParty.White);
                        break;
                    default:
                        // Kann nicht passieren, wird durch Einschränkung der Parameter verhindert
                        return;
                }
                return;
            }

            if ((this.OnMoveMade != null) && (raiseKingDeadEvent == false)) this.OnMoveMade();

            this.CheckForStandoff();
        }

        private void CheckForStandoff()
        {
            // Nur noch Bauern und Könige auf dem Brett
            bool standOff = true;

            List<csFig> figListW = this._board.GetAllActive(enumFigParty.White);
            foreach (csFig fig in figListW)
            {
                if (((fig.FigType != enumFigType.B)&&(fig.FigType != enumFigType.K))&&(standOff == true))
                {
                    standOff = false;
                }
            }

            List<csFig> figListB = this._board.GetAllActive(enumFigParty.Black);
            foreach (csFig fig in figListB)
            {
                if (((fig.FigType != enumFigType.B)&&(fig.FigType != enumFigType.K)) && (standOff == true))
                {
                    standOff = false;
                }
            }

            if ((OnStandOff != null)&&(standOff == true))
            {
                OnStandOff();
            }
        }
        
        public void placeFig(csBoard board, csFig fig)
        {
            board.placeFig(fig);
        }

        private void executeMove(csPossibleMove move)
        {
            this.UnmarkAllFields();
            this.UnmarkAllFigures();
            // Sonderbehandlung Bauer: Bauer führt 2 Schrittezug aus, daher wird 
            // EnPassantFlag gesetzt
            if (move.Fig.FigType == enumFigType.B)
            {
                if ((move.Field.Zeile - move.Fig.Zeile == 2) || (move.Fig.Zeile - move.Field.Zeile == 2))
                {
                    move.Fig.EnPassantGeradeAusgefuehrt = true;
                } else {
                    move.Fig.EnPassantGeradeAusgefuehrt = false;
                }
            }

            this._board.Move(move.Fig, move.Field.Spalte, move.Field.Zeile);

            // Sonderbehandlung Bauer: Bauer kommt auf der gegenerischen Seite an und wird zur Dame
            if (move.Fig.FigType == enumFigType.B)
            {
                if ((move.Fig.Party == enumFigParty.White) && (move.Fig.Zeile == 0))
                {
                    move.Fig.FigType = enumFigType.D;
                }
                if ((move.Fig.Party == enumFigParty.Black) && (move.Fig.Zeile == 7))
                {
                    move.Fig.FigType = enumFigType.D;
                }
            }

            move.Fig.ErsterZugGemacht = true;
        }

        public csField GetField(int spalte, int zeile)
        {
            csField field = this._board.GetField(spalte, zeile);
            return field;
        }

        public int CountFigs(enumFigParty party)
        {
            List<csFig> figList = this._board.GetAllActive(party);
            return figList.Count;
        }

        public string Translate(csPossibleMove move)
        {
            string result = "";

            switch (move.Fig.FigType)
            {
                case enumFigType.B:
                    result += "Bauer, ";
                    break;
                case enumFigType.D:
                    result += "Dame, ";
                    break;
                case enumFigType.K:
                    result += "König, ";
                    break;
                case enumFigType.L:
                    result += "Läufer, ";
                    break;
                case enumFigType.S:
                    result += "Springer, ";
                    break;
                case enumFigType.T:
                    result += "Turm, ";
                    break;
                default:
                    break;
            }

            if (move.Fig.Party == enumFigParty.Black) result += "schwarz, ";
            if (move.Fig.Party == enumFigParty.White) result += "weiss, ";

            string strStartSpalte = Tools.columnTranslation[move.Fig.Spalte];
            string strStartZeile = Convert.ToString(8 - move.Fig.Zeile);

            string strZielSpalte = Tools.columnTranslation[move.Field.Spalte];
            string strZielZeile = Convert.ToString(8 - move.Field.Zeile);

            result += strStartSpalte + strStartZeile + ",";
            result += strZielSpalte + strZielZeile;

            return result;
        }

    }
}
